
package com.joysuch.wwyt.dataperm.spring.listeners;

import java.lang.reflect.Field;

import javax.persistence.Entity;
import javax.persistence.Table;

import com.baomidou.mybatisplus.annotation.TableName;
import com.joysuch.wwyt.dataperm.annotation.DataPermissionField;
import com.joysuch.wwyt.dataperm.context.DataPermRegisterInfo;
import com.joysuch.wwyt.dataperm.context.DataPermRegisterInfoContext;
import com.joysuch.wwyt.dataperm.enums.DataPermissionFilterType;
import com.joysuch.wwyt.dataperm.spring.ClassScanListener;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.Select;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 数据权限注解扫描监听
 *
 */
public class DataPermAnnotationClassScanListener implements ClassScanListener {

    private static final Logger logger = LoggerFactory.getLogger("datapermission");

    @Override
    public void onScanClass(Class clazz) {
        if (hasAnnotation(clazz, Entity.class, TableName.class)) {
            scanForDataPermFields(clazz);
        }
    }

    private boolean hasAnnotation(Class clazz, Class... annotations) {
        for (Class a : annotations) {
            if (clazz.getAnnotation(a) != null) {
                return true;
            }
        }
        return false;
    }

    private void scanForDataPermFields(Class clazz) {
        Field[] fields = clazz.getDeclaredFields();
        for (Field f : fields) {
            DataPermissionField annotation = f.getAnnotation(DataPermissionField.class);
            if (annotation != null) {
                validateAnnotation(annotation, f);
                String tableName = getTableName(clazz);
                DataPermRegisterInfo info = new DataPermRegisterInfo();
                info.setType(annotation.type());
                info.setEntityClass(clazz);
                info.setFieldName(f.getName());
                info.setFilterSql(annotation.filterSql());
                info.setJoinInfo(annotation.joinInfo());
                info.setSqlColumn(annotation.sqlColumn());
                info.setTableName(tableName);
                logger.debug("发现数据权限注解:" + tableName + "." + annotation.sqlColumn() + " -> " + clazz.getName());
                DataPermRegisterInfoContext.add(info);
            }
        }
        // Method[] methods = clazz.getMethods();
    }

    /**
     * 校验注解是否正确:<br>
     * 1. type() = DataPermissionFilterType.JOIN_TABLE, joinInfo长度必须为3，且不能含空字符串(null, "", 全空白字符)<br>
     * 2. type() = DataPermissionFilterType.SQL, filterSql不能为空
     * 
     * @param annotation
     *                       注解
     * @param f
     *                       字段
     */
    private void validateAnnotation(DataPermissionField annotation, Field f) {
        if (annotation.type() == DataPermissionFilterType.JOIN_TABLE) {
            if (annotation.joinInfo().length != 3) {
                throw new IllegalArgumentException(
                        String.format("%s.%s joinInfo长度不为3, 请参考说明修正", f.getDeclaringClass().getName(), f.getName()));
            }
            if (hasEmptyString(annotation.joinInfo())) {
                throw new IllegalArgumentException(
                        String.format("%s.%s joinInfo含有null或空字符串", f.getDeclaringClass().getName(), f.getName()));
            }
        } else if (annotation.type() == DataPermissionFilterType.SQL) {
            if (annotation.filterSql().length() == 0 || annotation.filterSql().trim().length() == 0) {
                throw new IllegalArgumentException(String.format("%s.%s filterSql()值在type = SQL时不能为空字符串",
                        f.getDeclaringClass().getName(), f.getName()));
            }
            try {
                Statement parse = CCJSqlParserUtil.parse(annotation.filterSql().replace("${departs}", "1"));
                if (!(parse instanceof Select)) {
                    throw new RuntimeException(annotation.filterSql() + " 不是有效的select语句");
                }
            } catch (JSQLParserException e) {
                throw new RuntimeException(annotation.filterSql() + "解析错误", e);
            }
        }
    }

    private boolean hasEmptyString(String[] joinInfo) {
        for (String s : joinInfo) {
            if (s == null || s.trim().length() == 0) {
                return true;
            }
        }
        return false;
    }

    private String getTableName(Class clazz) {
        Table a = (Table) clazz.getAnnotation(Table.class);
        if (a != null) {
            return a.name();
        }
        TableName tn = (TableName) clazz.getAnnotation(TableName.class);
        if (tn != null) {
            return tn.value();
        }
        return null;
    }
}
